#ifndef DNSCACHE_H
#define DNSCACHE_H

#include <map>
#include <vector>
#include <string>
#include <time.h>
#include "dnsPacket.h"
#include "threads.h"
#include <deque>

class CacheListNode
{
	public:
		CacheListNode() {}
		virtual ~CacheListNode() {}
		void setPrevious( CacheListNode *node ) { previous_ = node; }
		CacheListNode* previous() const { return previous_; }
		void setNext( CacheListNode *node ) { next_ = node; } 
		CacheListNode* next() const { return next_; }
		void setRecords( const std::vector< DNSRecord > &records ); 
		std::vector< DNSRecord > records() const { return records_; }
		time_t expiration() const { return expiration_; }
		void setExpiration( const uint32_t &seconds );
		void setQuery( const std::string &query ) { query_ = query; }
		std::string query() const { return query_.toSTDString(); }
		uint64_t memorySize() const
		{
			uint64_t size = 0;
			size += sizeof( time_t ) + sizeof( previous_ ) + sizeof( next_ ) + sizeof( records_ );
			for( uint32_t i = 0; i < records_.size(); ++i )
			{
				size += records_[i].memorySize();
			}
			size += query_.memorySize();
			return size;
		}

	private:
		time_t expiration_;
		CacheListNode* previous_;
		CacheListNode* next_;
		std::vector< DNSRecord > records_;
		StaticBuffer query_;
};

class DNSCache
{
	public:
		DNSCache(); 
		virtual ~DNSCache();

		void setMaxSize( const uint64_t &size ); 
		uint64_t maxSize() const;
		uint64_t size() const;
		void addAnswers( const std::string& question, std::vector< DNSRecord > rs );
		void addAuthorities( const std::string& domain, std::vector< DNSRecord > auths );
		std::vector< DNSRecord > answers( DNSQuestion q ) const;
		std::vector< DNSRecord > authorities( DNSQuestion q ) const;
		void setMaxTTL( const uint32_t &ttl );
		void maxTTL() const;
		//! Removes all expired entries in the cache
		//! This is meant to be called from a thread that is only meant to do maintainance on the cache. 
		void cleanCache();

	protected:
		//! when the cache grows to large this function can remove the older entries
		//! in the cache. Additionally this function is to be called by functions
		//! that already hold the lock and therefore doesn't lock.
		void overSize();
		//! Should ONLY be called from cleanCache()
		void removeBuckets( const int &num );
		//!helper function (Could probably be moved out into a helper module)
		std::vector< std::string > split( const std::string &s, const char &delimiter ) const;

		//! Adds a node to the ttl list so that it can be removed from the cache
		//! once expired. Additionally because this function is to be called from
		//! functions that hold the lock it does not preform any locking.  
		void addToTTLList( CacheListNode* node );
		//! Doesn't lock but removes a node
		void removeNode( CacheListNode* node );
		//! doesn't lock removes the node if somehow the ttl already expired.
		uint32_t getTTLIndex( CacheListNode* node );
		//! doesn't lock
		void resizeSortedTTLList(); 


	private:
		mutable ReaderWriterLock recordLock_;
		uint64_t size_; //in B
		uint64_t maxSize_; //in B
		uint32_t maxTTL_;
		CacheListNode* newestRecord_;
		CacheListNode* oldestRecord_;
		time_t startTime_;
		int secondsPerBucket_;
		std::deque< std::vector<CacheListNode*> > sortedTTLs_; //insert quick with binary search
		std::map< std::string, CacheListNode* > answers_;
		std::map< std::string, CacheListNode* > authorities_;
};

#endif
